package com.piece.core.framework.util.net;

import com.piece.core.framework.support.convert.LambdaOptional;
import com.piece.core.framework.util.string.StringUtil;
import lombok.extern.slf4j.Slf4j;
import java.net.*;
import java.util.Enumeration;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 网络工具
 */
@Slf4j
public class NetUtil {

    /**
     * 本机名称
     */
    private static String LOCAL_HOST_NAME;
    /**
     * 本机地址
     */
    private static String LOCAL_HOST_ADDRESS;
    /**
     * 本机默认物理网卡
     */
    private static NetworkInterface DEFAULT_NETWORK_INTERFACE;
    /**
     * 最大端口号
     */
    public static int MAX_PORT = 2 << 15;
    /**
     * 本机IP地址
     */
    public static String LOCAL_IP = "127.0.0.1";
    /**
     * 本机HOST地址
     */
    public static String LOCAL_HOST = "localhost";
    /**
     * IPv4地址正则表达式
     */
    private static String IP_REGEX = "(\\d{0,3}\\.){3}\\d{0,3}";
    /**
     * A类私用地址
     * <p>A类地址范围：0.0.0.0-127.255.255.255</p>
     * <p>默认子网掩码：255.0.0.0</p>
     */
    private static long NATIVE_A_IP_BEGIN = encodeIpToLong("10.0.0.0");

    private static long NATIVE_A_IP_END = encodeIpToLong("10.255.255.255");
    /**
     * B类私用地址
     * <p>B类地址范围：128.0.0.0-191.255.255.255</p>
     * <p>默认子网掩码：255.255.0.0</p>
     */
    private static long NATIVE_B_IP_BEGIN = encodeIpToLong("172.16.0.0");

    private static long NATIVE_B_IP_END = encodeIpToLong("172.31.255.255");
    /**
     * C类私用地址
     * <p>C类地址范围：192.0.0.0-223.255.255.255</p>
     * <p>默认子网掩码：255.255.255.0</p>
     */
    private static long NATIVE_C_IP_BEGIN = encodeIpToLong("192.168.0.0");

    private static long NATIVE_C_IP_END = encodeIpToLong("192.168.255.255");
    /**
     * 本地回环地址
     */
    private static long NATIVE_L_IP_BEGIN = encodeIpToLong("127.0.0.0");

    private static long NATIVE_L_IP_END = encodeIpToLong("127.255.255.255");

    static {
        final LambdaOptional<String> localHostName = LambdaOptional.newInstance();
        final LambdaOptional<String> localHostAddress = LambdaOptional.newInstance();
        final LambdaOptional<NetworkInterface> defaultNetworkInterface = LambdaOptional.newInstance();
        try {
            final AtomicInteger index = new AtomicInteger(Integer.MAX_VALUE);
            // 处理多个物理网卡和虚拟网卡
            Enumeration enumeration = NetworkInterface.getNetworkInterfaces();
            NetworkInterface networkInterface = null;
            while (enumeration.hasMoreElements()) {
                networkInterface = (NetworkInterface) enumeration.nextElement();
                int nowIndex = networkInterface.getIndex();
                NetworkInterface finalNetworkInterface = networkInterface;
                networkInterface.getInterfaceAddresses().forEach(interfaceAddress -> {
                    InetAddress inetAddress = interfaceAddress.getAddress();
                    if (index.get() > nowIndex && // 索引最小网卡
                            inetAddress.isSiteLocalAddress() && // 本机地址
                            !inetAddress.isAnyLocalAddress() && // 通配地址
                            !inetAddress.isLoopbackAddress() && // 回环地址
                            !inetAddress.isLinkLocalAddress() && // 连接地址：虚拟网卡
                            !inetAddress.isMulticastAddress() // 广播地址
                    ) {
                        index.set(nowIndex);
                        localHostAddress.set(inetAddress.getHostAddress());
                        defaultNetworkInterface.set(finalNetworkInterface);
                    }
                });
            }
        } catch (SocketException e) {
            log.error("初始化本机网络信息异常:{}", e.getMessage());
        }
        try {
            localHostName.set(InetAddress.getLocalHost().getHostName());
        } catch (UnknownHostException e) {
            log.error("初始化本机名称异常:{}", e.getMessage());
        }
        LOCAL_HOST_NAME = localHostName.get();
        LOCAL_HOST_ADDRESS = localHostAddress.get();
        DEFAULT_NETWORK_INTERFACE = defaultNetworkInterface.get();
    }

    private NetUtil() {
        throw new IllegalStateException("对象禁止实例化");
    }

    /**
     * 端口编码
     */
    public static short encodePort(int port) {
        return (short) port;
    }

    /**
     * 端口解码
     */
    public static int decodePort(short port) {
        return Short.toUnsignedInt(port);
    }

    /**
     * IP地址编码
     */
    public static int encodeIpToInt(String ip) {
        return (int) encodeIpToLong(ip);
    }

    /**
     * IP地址编码
     */
    public static long encodeIpToLong(String ip) {
        long result = 0, value;
        final String[] array = ip.split("\\.");
        for (int index = 3; index >= 0; index--) {
            value = Long.parseLong(array[3 - index]);
            result |= value << (index * 8);
        }
        return result;
    }

    /**
     * IP地址解码
     */
    public static String decodeIntToIp(int value) {
        return decodeLongToIp(Integer.toUnsignedLong(value));
    }

    /**
     * IP地址解码
     */
    public static String decodeLongToIp(long value) {
        return ((value >> 24) & 0xFF) + "." +
                        ((value >> 16) & 0xFF) + "." +
                        ((value >> 8) & 0xFF) + "." +
                        (value & 0xFF);
    }

    /**
     * IPv6地址编码
     */
    public static byte[] encodeIPv6(String ip) {
        try {
            return InetAddress.getByName(ip).getAddress();
        } catch (UnknownHostException e) {
            log.error("IPv6地址编码异常：{}", e.getMessage());
        }
        return null;
    }

    /**
     * IPv6地址解码
     */
    public static String decodeIPv6(byte[] value) {
        try {
            return InetAddress.getByAddress(value).getHostAddress();
        } catch (UnknownHostException e) {
            log.error("IPv6地址解码异常：{}", e.getMessage());
        }
        return null;
    }

    /**
     * 本机名称
     */
    public static String localHostName() {
        return LOCAL_HOST_NAME;
    }

    /**
     * 本机地址
     */
    public static String localHostAddress() {
        return LOCAL_HOST_ADDRESS;
    }

    /**
     * 本机默认物理网卡
     */
    public static NetworkInterface defaultNetworkInterface() {
        return DEFAULT_NETWORK_INTERFACE;
    }

    /**
     * 是否是IP地址
     */
    public static boolean isIp(String host) {
        return StringUtil.regex(host, IP_REGEX, true);
    }

    /**
     * 是否是本地IP地址
     */
    public static boolean isLocalIp(String host) {
        long value = encodeIpToLong(host);
        return (NATIVE_A_IP_BEGIN <= value && value <= NATIVE_A_IP_END) ||
                        (NATIVE_B_IP_BEGIN <= value && value <= NATIVE_B_IP_END) ||
                        (NATIVE_C_IP_BEGIN <= value && value <= NATIVE_C_IP_END) ||
                        (NATIVE_L_IP_BEGIN <= value && value <= NATIVE_L_IP_END);
    }

    /**
     * 创建Socket地址
     */
    public static InetSocketAddress buildSocketAddress(int port) {
        return buildSocketAddress(null, port);
    }

    /**
     * 创建Socket地址
     *
     * @param host 地址
     * @param port 端口
     */
    public static InetSocketAddress buildSocketAddress(final String host, final int port) {
        if (StringUtil.isEmpty(host)) {
            return new InetSocketAddress(port);
        } else {
            return new InetSocketAddress(host, port);
        }
    }
}